# coding: utf-8
# @Author: 莫小鱼
import os
from datetime import datetime
# import pytesseract
# from PIL import Image
from hamcrest import assert_that, equal_to
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common import NoSuchElementException, TimeoutException
from AutoWebTest.config import setting
from AutoWebTest.config.setting import WAIT_TIME
from AutoWebTest.utils.log_util import logs
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from AutoWebTest.utils.handle_data.configParse import ConfigParse


class BasePage(object):
    """
    封装浏览器的一些操作，对象webdriver里面的一些方法进行二次开发
    此类封装所有的操作，所有页面继承该类
    """

    def __init__(self, driver):
        # 浏览器对象初始化
        self.__driver = driver
        # 显示等待初始化
        self.__wait = WebDriverWait(self.__driver, WAIT_TIME)
        # 加载config.ini文件方法
        self.conf = ConfigParse()

    def window_max(self):
        """浏览器窗口最大化"""
        self.__driver.maximize_window()

    def window_full(self):
        """浏览器全屏窗口"""
        self.__driver.fullscreen_window()

    def screenshot(self):
        """浏览器截屏"""
        self.__driver.get_screenshot_as_png()

    def open_url(self, url):
        """打开测试页面"""
        if url.startswith('http') or url.startswith('https'):
            self.__driver.get(url)
            logs.info(f'打开页面：{url}')
        else:
            new_url = self.conf.get_host('host') + url
            self.__driver.get(new_url)
            logs.info(f'打开页面：{new_url}')

    def refresh(self):
        """页面刷新"""
        self.__driver.refresh()

    @property
    def switch_to(self):
        """切换switch_to iframe/alert"""
        return self.__driver.switch_to

    def iframe(self, frame):
        """切换到iframe框架中"""
        try:
            self.switch_to.frame(frame)
        except:
            logs.error("切换iframe框架失败")

    def switch_to_new_tab(self):
        """浏览器打开新的标签页，切换窗口句柄"""
        try:
            original_window = self.__driver.window_handles[0]  # 获取当前窗口句柄作为原始窗口
            all_window = self.__driver.window_handles  # 获取所有窗口句柄列表
            new_window = None
            for window in all_window:
                if window != original_window:  # 找到不等于原始窗口的句柄
                    new_window = window
                    break
            if new_window:
                self.switch_to.window(new_window)  # 切换到新窗口句柄
                logs.info("成功切换到新标签页")
        except TimeoutException:
            logs.error("等待新标签页打开超时")
        except NoSuchElementException:
            logs.error("未找到新的标签页句柄")
        except Exception as e:
            logs.error(f"切换新窗口失败：{str(e)}")

    def switch_to_tab_by_index(self,index):
        """
        需要切换多个标签页时使用，比如三个以上，用索引选择句柄
        :param index:
        :return:
        """
        try:
            all_window = self.__driver.window_handles  # 获取所有句柄
            if 0 <= index < len(all_window):
                target_window = all_window[index]      # 获取目标窗口句柄
                self.switch_to.window(target_window)   # 切换到目标窗口
                logs.info(f"成功切换到第{index+1}个标签页")
            else:
                logs.error("指定的窗口索引超出操作范围")
        except Exception as e:
            logs.error(f"切换窗口时发送异常：{e}")

    def exit_iframe(self):
        """退出iframe内联框架"""
        self.switch_to.default_content()

    @property
    def alert(self):
        """alert弹窗处理"""
        return self.__wait.until(ec.alert_is_present())  # 判断弹框是否存在，等到弹框出现为止

    def alert_confirm(self):
        """点击alert弹框确认操作"""
        self.alert.accept()

    def alert_cancel(self):
        """点击弹窗alert取消操作"""
        self.alert.dismiss()

    def location_element(self, by, value):
        """
        二次封装find_element方法，定位页面元素
        :param by: 定位方式，比如By.ID, By.XPATH
        :param value: 定位表达式
        :return:
        """
        try:
            element = self.__wait.until(ec.presence_of_element_located((by, value)))  # 等待元素出现
            logs.info(f"找到元素：{by}={value}")
            return element
        except Exception as e:
            logs.error(f"未找到元素：{by}={value}")
            raise e

    def location_elements(self, by, value):
        """
        二次封装find_elements方法，定位页面元素列表
        :param by: 定位方式，比如By.ID, By.XPATH
        :param value: 定位表达式
        :return:
        """
        try:
            self.__wait.until(ec.presence_of_all_elements_located((by, value)))  # 等待元素出现
            elements = self.__driver.find_elements(by, value)
            logs.info(f"找到元素列表：{by}={value}")
            return elements
        except Exception as e:
            logs.error(f"未找到元素列表：{by}={value}")
            raise e

    def click(self, locator: tuple, force=False):
        """
        封装点击操作
        :param locator:（tuple）定位元素信息，等于（By.ID,'xxx'）
        :param force: 可选参数，表示是否使用强制点击，默认为False
        :return:
        """
        try:
            element = self.location_element(*locator)
            if not force:
                self.__driver.execute_script("arguments[0].click()", element)
            else:
                self.__driver.execute_script("arguments[0].click({force:true})", element)
            logs.info(f"元素被点击：{locator}")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位：{e}")
            raise e

    def send_keys_action(self,locator: tuple,data):
        """
        模拟键盘的输入操作:先鼠标移到相应位置，在执行输入内容
        :param locator: 定位表达式
        :param data: 输入的内容
        :return:
        """
        try:
            element = self.location_element(*locator)
            ActionChains(self.__driver).move_to_element(element).click().send_keys(data).perform()
        except NoSuchElementException as e:
            logs.error(f"未找到元素：{e}")

    def scroll_to_button(self):
        """
        使用javascript滚动页面到最底部
        :return:
        """
        self.__driver.execute_script("window.scrolTo(0,document.body.scrollHeight);")

    def send_keys(self, locator: tuple, data):
        """
        封装输入操作，对send_keys进行的二次封装
        :param locator: （tuple）定位元素信息，等于（By.ID,'xxx'）
        :param data: 要输入的内容
        :return:
        """
        try:
            element = self.location_element(*locator)
            element.send_keys(data)
            logs.info(f"元素被输入内容：{locator},输入内容为：{data}")
        except NoSuchElementException as e:
            logs.error(f"元素无法被定位：{e}")
            raise e

    def enter(self):
        """
        封装键盘回车操作
        :return:
        """
        try:
            ActionChains(self.__driver).send_keys(Keys.ENTER).perform()
            logs.info("执行回车操作")
        except NoSuchElementException as e:
            logs.error(f"元素无法被定位：{e}")
            raise e

    def right_click(self, locator: tuple):
        """
        鼠标右键点击操作
        :param locator: （tuple）定位页面元素
        :return:
        """
        try:
            element = self.location_element(*locator)
            ActionChains(self.__driver).context_click(element).perform()
            logs.info(f"执行鼠标右键点击操作:{locator}")
        except NoSuchElementException as e:
            logs.error(f"元素无法被定位：{e}")
            raise e

    def double_click(self, locator: tuple):
        """
        封装鼠标双击操作
        :param locator: （tuple）定位页面元素
        :return:
        """
        try:
            element = self.location_element(*locator)
            ActionChains(self.__driver).double_click(element).perform()
            logs.info(f"执行鼠标双击操作:{locator}")
        except NoSuchElementException as e:
            logs.error(f"元素无法被定位：{e}")
            raise e

    def screenshots(self, image_name):
        """
        封装截图的方法
        :param image_name: 文件名
        :return:
        """
        current_time = datetime.now().strftime("%Y%m%d%H%M%S")  # 获取当前的时间
        file_name = f'{image_name}-{current_time}.png'
        file_path = os.path.join(setting.FILE_PATH.get('screenshots'), file_name)
        self.__driver.get_screenshot_as_file(file_path)

    def screenshots_png(self):
        """
        页面截屏，保存为png格式
        :return:
        """
        return self.__driver.get_screenshot_as_png()

    def clear(self, locator: tuple):
        """
        清空输入框内容
        :param locator: （tuple）定位元素
        :return:
        """
        try:
            element = self.location_element(*locator)
            element.clear()
            logs.info(f"清空输入框内容:{locator}")
        except NoSuchElementException as e:
            logs.error(f"元素无法被定位：{e}")
            raise e

    def selects(self, locator: tuple, index):
        """
        封装下拉菜单选择
        :param locator: 元素定位表达式
        :param index: 要选择的下拉选项
        :return:
        """
        try:
            select = Select(self.location_element(*locator))
            select.select_by_index(index)
        except NoSuchElementException as e:
            logs.error(f"元素未找到：{e}")
            raise AssertionError("元素不存在")

    @property
    def current_url(self):
        """获取当前页面的url  属性方法可以直接调用函数名获取"""
        return self.__driver.current_url

    @property
    def title(self):
        """获取页面标题"""
        return self.__driver.title

    # def ocr_capcha(self, locator: tuple):
    #     """
    #     1、定位到图形验证码，保存图片
    #     2、调用Image去打开图像
    #     3、调用pytesseract模块dddocr模块进行ocr识别
    #     :param locator:
    #     :return:
    #     """
    #     capcha_element = self.location_element(*locator)
    #     # 截取图形验证码
    #     capcha_path = setting.FILE_PATH['screenshots'] + '/captcha.png'
    #     capcha_element.screenshots(capcha_path)
    #     # 调用Image去打开图像
    #     capcha_image = Image.open(capcha_path)
    #     try:
    #         # 调用pytesseract进行ocr识别
    #         capcha_text = pytesseract.image_to_string(capcha_image)
    #         logs.info(f"识别到的验证码为：{capcha_text}")
    #         return capcha_text
    #     except pytesseract.pytesseract.TesseractNotFoundError:
    #         logs.error("找不到tesseract,未安装引擎")

    def is_element_present(self, locator: tuple):
        """
        判断元素是否存在
        :param locator: 定位表达式
        :return:
        """
        try:
            self.__wait.until(ec.presence_of_element_located(*locator))
            return True
        except:
            return False

    def assert_is_element_present(self, locator: tuple):
        """
        判断元素是否存在
        :param locator: 元素定位表达式
        :return:
        """
        try:
            element = self.__driver.find_element(*locator)
            assert element.is_displayed(), "元素不存在"
        except NoSuchElementException as e:
            logs.error(f"元素未找到：{e}")
            raise AssertionError("元素不存在")

    def assert_title(self, expect_title):
        """
        断言预期标题文本是否包含在实际文本页面标题中
        :param expect_title: 预期文本标题
        :return:
        """
        assert expect_title in self.title

    def assert_element_text(self, locator: tuple, expect_text):
        """
        断言预期元素的文本信息是否正确
        :param locator: 元素定位表达式
        :param expect_text: 预期的文本
        :return:
        """
        try:
            # element = self.__driver.find_element(*locator)
            element = self.__wait.until(ec.presence_of_element_located(locator))
            assert_that(element.text, equal_to(expect_text))
            logs.info(f"获取到的文本为：{element.text};期望文本为：{expect_text}")
        except NoSuchElementException as e:
            logs.error(f"元素未找到：{e}")
            raise AssertionError("元素不存在")

    def assert_element_not_visible(self, locator: tuple):
        """
        断言元素不可见，不存在
        :param locatol:
        :return:
        """
        try:
            self.__wait.until(ec.invisibility_of_element(locator))
        except TimeoutException:
            logs.error("元素可见")

    def assert_alert_present(self):
        """
        断言页面内是否出现alert弹窗
        :return:
        """
        try:
            alert = self.__wait.until(ec.alert_is_present())
            return True
        except TimeoutException:
            return False




